as.fx = (function() {
	var tTime = 1000, tDelay = 5;
	var basicDelta = function(progress) {
		return Math.pow(progress,6);
	}
	var delta = function(progress) {
		return progress < 0.5 ? basicDelta(2 * progress) / 2 : (2 - basicDelta(2 * (1 - progress))) / 2;
	}
	var units = ['px','em','%'];
	var constProperties = ['border-style'];
	var pxProperties = ['width','height','left','top','padding','margin','padding-top','padding-right','padding-bottom','padding-left','margin-top','margin-right','margin-bottom','margin-left','border-width','border-top-width','border-right-width','border-bottom-width','border-left-width'];
	
	/*** color functions and constants ***/
	var colorProperties = ['color','background-color','border-top-color','border-right-color','border-bottom-color','border-left-color'];
	var colors = {
		'red': [255,0,0],
		'yellow': [255,255,0],
		'blue': [0,0,255],
		'green': [0,128,0],
		'cyan': [0,255,255],
		'magenta': [255,0,255],
		'white': [255,255,255],
		'black': [0,0,0]
	}
	var hex2rgb = function(hex) {
		hex = hex.replace("#","").split("");
		var colors = [], shift = hex.length == 6 ? 1 : 0, multiplier = shift ? 2 : 1;
		for (var i=0;i<3;i++) colors.push(parseInt(hex[i * multiplier] + hex[i * multiplier + shift],16));
		return colors;
	}
	var listRGB = function(rgb) {
		return as.map(
			rgb.replace(/\s*/gi,"").replace("rgb(","").replace(")","").split(","),
			function(item) { return parseInt(item)}
		);
	}
	var diffRGB = function(from,to,dx) {
		var now = [];
		for (var color=0;color<3;color++) now[color] = parseInt(from[color] + Math.round((to[color] - from[color]) * dx),10);
		return now;
	}
	var rgbRE = /(\:?\s*)rgb\(((\d){1,3}\s*,?\s*){3}\)(\s|$)/;
	var hexRE = /(\:?\s*)#?(([0-9A-Fa-f]){3}){1,2}(\s|$)/;
	/*** \\color functions and constants\\ ***/
	
	/*** converters from short to full values ***/
	var s2fd = function(value) {
		var values = value.split(" "), left, top, right, bottom;
		switch(values.length) {
			case 1:
			left = top = right = bottom = values[0];
			break;
			
			case 2:
			left = right = values[1]; top = bottom = values[0];
			break;
			
			case 3:
			top = values[0]; left = right = values[1]; bottom = values[2];
			
			case 4:
			top = values[0]; right = values[1]; bottom = values[2]; left = values[3];
		}
		return {top: top, right: right, bottom: bottom, left: left}
	}
	var s2f = {
		'border': function(border) {
			border = border.split(" ");
			var width = border[0], style = border[1], color = border[2] || "";
			return {
				'border-top-width': width, 'border-right-width': width, 'border-bottom-width': width, 'border-left-width': width,
				'border-top-color': color, 'border-right-color': color, 'border-bottom-color': color, 'border-left-color': color,
				'border-style': style
			};
		},
		'margin': function(margin) {
			var values = s2fd(margin);
			return {'margin-top': values.top, 'margin-right': values.right, 'margin-bottom': values.bottom, 'margin-left': values.left};
		},
		'padding': function(padding) {
			var values = s2fd(padding);
			return {'padding-top': values.top, 'padding-right': values.right, 'padding-bottom': values.bottom, 'padding-left': values.left};
		},
		'background': function(bg) {
			var colorRE;
			if (bg.match(rgbRE)) return {'background-color': bg.match(rgbRE)[0]};
			else if (bg.match(hexRE)) return {'background-color': bg.match(hexRE)[0]};
			else {
				for (var c in colors) {
					colorRE = new RegExp("\:?\s*" + c + "(\s|$)");
					if (bg.match(colorRE)) return {'background-color': c};
				}
			}
			return false;
		}
	}
	var convertToOneUnit = function(from,to,s,element) {
		var pxValue;
		switch(from.unit) {
			case "px": pxValue = from.value;
			break;
			
			case "%": pxValue = from.value * parseInt(as.style(element.parentNode,s == "height" ? "height" : "width")) * 100;
			break;
			
			case "em": pxValue = s.value * parseInt(as.style(element,'font-size'));
			break;
		}
		switch(to.unit) {			
			case "px": from.value = pxValue;
			break;
			
			case "%": from.value = pxValue / parseInt(as.style(element.parentNode,s == "height" ? "height" : "width")) * 100;
			break;
			
			case "em": from.value = pxValue / parseInt(as.style(element,'font-size'));
			break;
		}
		from.unit = to.unit;
	}
	/*** \\converters from short to full values\\ ***/
	
	var parseProp = function(styles,name,value,element) {
		var oStyle, fStyle, currentStyle;
		if (s2f[name]) {
			fStyle = s2f[name](value);
			if (!fStyle) return;
			for (var f in fStyle) parseProp(styles,f,fStyle[f],element);
			return;
		}
		oStyle = {};
		if (as.inArray(constProperties,name) != -1) {
			styles[name] = {
				value: value,
				type: "const"
			};
			return;
		}
		for (var u=0,l=units.length;u<l;u++) {
			uRE = new RegExp(units[u] + "$","gi");
			if (value.match(uRE)) {
				oStyle.value = parseFloat(value.replace(uRE,"")) || 0;
				oStyle.unit = units[u];
			}			
		}
		if (!oStyle.unit && as.inArray(pxProperties,name) != -1) {
			if (value == "auto") {
				currentStyle = as.css(element,name);
				as.css(element,name,"auto");
				value = as.css(element,name);
				as.css(element,name,currentStyle);
			}
			
			oStyle.value = parseFloat(value) || 0;
			oStyle.unit = "px";
		}
		oStyle.unit && (styles[name] = oStyle);
		if (as.inArray(colorProperties,name) != -1) {
			if (colors[value]) oStyle.value = colors[value];
			if (value.match(hexRE)) oStyle.value = hex2rgb(value);
			if (value.match(rgbRE)) oStyle.value = listRGB(value);
			
			oStyle.value && (oStyle.type = 'color') && (styles[name] = oStyle);
		}
	}
	var parseStyle = function(style,element) {
		var styles = {}, oStyle, uRE;
		for (var s in style) {
			parseProp(styles,s,style[s],element);
		}
		return styles;
	}
	var getFrom = function(element,to,from) {
		var styles = [], inFrom = {};
		for (var s in to) {
			if (!from[s]) styles.push(s);
			else inFrom[s] = from[s];
		}
		styles = styles.length ? as.css(element,styles) : {};
		for (var s in inFrom) styles[s] = inFrom[s];
		return parseStyle(styles,element);
	}
	var t = function(style) {
		var t = {};
		for (var s in style) t[s] = style[s].value;
		return t;
	}
	var transform = function(data) {
		var progress = (+ new Date - data.ast) / data.time, dx, now;
		if (progress < 1) {
			dx = delta(progress).toFixed(2);
			now = data.transform ? data.transform(data.from,data.to,dx) : (function(from,to,dx) {
				var now = {};
				for (var i in from) now[i] = from[i] + (to[i] - from[i]) * dx;
				return now;
			})(data.from,data.to,dx);
			data.oneach && data.oneach(now);
			setTimeout(function() {transform(data)}, tDelay);
			return;
		}
		data.oneach && data.oneach(data.to);
		data.callback && data.callback.call(data.context || window);
	}
	var fx = {
		transform: function(data) {
			if (!data) return;
			var from = data.from, to = data.to, cache = {},
				callback = data.callback, context = data.context || window,
				time = data.time || tTime, ast = (+new Date + (data.delay || 0));
				
			if (!from || !to) return;
			for (var f in from) {
				cache[f] = from[f];
			}
			transform({
				from: cache, to: to,
				callback: callback, context: context,
				time: time, ast: ast,
				oneach: function(now) {
					for (var f in now) {
						from[f] = now[f];
					}
				}
			});
		},
		animate: function(element,to,data) {	
			if (!element || !to) return;
			data = data || {};
			var from = data.from || {}, to = parseStyle(to,element), cst = {},
				callback = data.callback, context = data.context || window,
				time = data.time || tTime, ast = (+new Date + (data.delay || 0));
			for (var s in to) to[s].type == "const" && (cst[s] = to[s]) && delete to[s];
			from = getFrom(element,to,from);
			for (var s in from) {
				(as.inArray(pxProperties,s) != -1) && (from[s].unit != to[s].unit) && convertToOneUnit(from[s],to[s],s,element);
				as.style(element,s,to[s].value);
				if (from[s].unit == to[s].unit && from[s].value == to[s].value) {
					delete from[s];
					delete to[s];
				}
			}
			for (var s in cst) as.css(element,s,cst[s].value);
			transform({
				from: from, to: to,
				callback: callback, context: context,
				time: time, ast: ast,
				transform: function(from, to, dx) {
					var now = {};
					for (var f in from) {
						now[f] = {
							value: from[f].type == "color" ? diffRGB(from[f].value,to[f].value,dx) : from[f].value + (to[f].value - from[f].value) * dx,
							type: from[f].type
						}
					}
					return now;
				},
				oneach: function(now) {
					var styles = {};
					for (var s in now) {
						if (now[s].type == "color") styles[s] = "rgb(" + now[s].value + ")";
						else styles[s] = now[s].value + to[s].unit;
					}
					as.style(element,styles);
				}
			});
		}
	}
	return fx;
})();